
package org.yuanqiframework.yuanqi.boot.generator;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.time.*;
import java.util.*;

public class TypeScriptGenerator {
    // 已处理的类，避免重复处理和循环引用
    private static final Set<Class<?>> PROCESSED_CLASSES = new HashSet<>();
    // 在类的字段定义区域添加新的数据结构
    // 存储按包分类的接口定义，保持包的顺序
    private static final Map<String, Map<String, String>> PACKAGE_INTERFACE_DEFINITIONS = new LinkedHashMap<>();

    public static void main(String[] args) {
        generateTypeScriptInterfaces("out/types", "d.ts", "org.yuanqiframework.yuanqi.boot", new String[]{"sys"}, new String[]{"entity", "vo", "dto"});

//        generateTypeScriptInterfaces("com.cqsoul.boot", new String[]{"sys", "exam"}, new String[]{"entity", "vo", "dto"}, true);
    }

    /**
     * @param scanBasePackages        com.cqsoul.boot
     * @param scanBaseModules         ["sys"]
     * @param scanPackages            ["entity", "vo", "dto"]
     * @param generateFrameworkModule true
     */
    public static synchronized void generateTypeScriptInterfaces(String scanBasePackages, String[] scanBaseModules, String[] scanPackages, boolean generateFrameworkModule) {
        generateTypeScriptInterfaces("out/types", "d.ts", scanBasePackages, scanBaseModules, scanPackages, generateFrameworkModule);
    }

    /**
     * 生成TypeScript接口定义
     * <p>
     * //可以在项目启动后自动调通该方法，dir指定为前端的types目录下
     *
     * @param dir                     out/types
     * @param fileType                ts
     * @param scanBasePackages        com.cqsoul.boot
     * @param scanBaseModules         ["sys"]
     * @param scanPackages            ["entity", "vo", "dto"]
     * @param generateFrameworkModule true
     */
    public static synchronized void generateTypeScriptInterfaces(String dir, String fileType, String scanBasePackages, String[] scanBaseModules, String[] scanPackages, boolean generateFrameworkModule) {
        if (generateFrameworkModule) {
            generateTypeScriptInterfaces(dir, fileType, "org.yuanqiframework.yuanqi.boot", new String[]{"_sys"}, new String[]{"entity", "vo", "dto"});
        }
        generateTypeScriptInterfaces(dir, fileType, scanBasePackages, scanBaseModules, scanPackages);
    }

    /**
     * 生成TypeScript接口定义
     * <p>
     * //可以在项目启动后自动调通该方法，dir指定为前端的types目录下
     *
     * @param dir              out/types
     * @param fileType         ts
     * @param scanBasePackages com.cqsoul.boot
     * @param scanBaseModules  ["sys"]
     * @param scanPackages     ["entity", "vo", "dto"]
     */
    public static synchronized void generateTypeScriptInterfaces(String dir, String fileType, String scanBasePackages, String[] scanBaseModules, String[] scanPackages) {

        // 清空之前的状态
        PROCESSED_CLASSES.clear();
        // 初始化包分类存储
        PACKAGE_INTERFACE_DEFINITIONS.clear();
        for (String pkg : scanPackages) {
            PACKAGE_INTERFACE_DEFINITIONS.put(pkg, new LinkedHashMap<>());
        }

        // 如果有多个模块，分别生成文件
        for (String moduleName : scanBaseModules) {
            generateModuleInterfaces(dir, fileType, scanBasePackages, moduleName, scanPackages);
        }
    }

    /**
     * 为单个模块生成TypeScript接口定义
     */
    private static void generateModuleInterfaces(String dir, String fileType, String scanBasePackages, String moduleName, String[] scanPackages) {
        // 清空之前的状态
        PROCESSED_CLASSES.clear();
        // 初始化包分类存储
        PACKAGE_INTERFACE_DEFINITIONS.clear();
        for (String pkg : scanPackages) {
            PACKAGE_INTERFACE_DEFINITIONS.put(pkg, new LinkedHashMap<>());
        }

        // 用于存储所有扫描到的类
        List<Class<?>> classes = new ArrayList<>();

        // 收集所有类和包路径映射
        for (String shortPackageName : scanPackages) {
            String fullPackageName = scanBasePackages + "." + moduleName + "." + shortPackageName;
            try {
                classes.addAll(getClasses(fullPackageName));
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        // 先收集所有需要处理的类（包括依赖的类）
        for (Class<?> clazz : classes) {
            collectClassDependencies(clazz);
        }

        // 生成所有接口定义，并按包分类存储
        for (Class<?> clazz : PROCESSED_CLASSES) {
            if (clazz.isInterface() || clazz.isEnum()) {
                continue; // 暂时不处理接口和枚举
            }
            String interfaceDefinition = generateInterface(clazz);
            if (interfaceDefinition != null && !interfaceDefinition.isEmpty()) {
                String interfaceName = "I" + clazz.getSimpleName();

                // 确定类属于哪个包
                String classPackage = clazz.getPackage().getName();
                String packageKey = null;

                // 查找该类属于哪个扫描包
                for (String scanPackage : scanPackages) {
                    String tempPath = scanBasePackages + "." + moduleName + "." + scanPackage;
                    if (classPackage.equals(tempPath) || classPackage.startsWith(tempPath + ".")) {
                        packageKey = scanPackage;
                        break;
                    }
                }

                // 根据包分类存储接口定义
                if (packageKey != null) {
                    PACKAGE_INTERFACE_DEFINITIONS.get(packageKey).put(interfaceName, interfaceDefinition);
                } else {
                    // 如果不属于任何指定的包，可以放到第一个包或其他处理方式
                    if (scanPackages.length > 0) {
                        PACKAGE_INTERFACE_DEFINITIONS.get(scanPackages[0]).put(interfaceName, interfaceDefinition);
                    }
                }
            }
        }
        StringBuilder dataFrom = new StringBuilder();
        for (String scanPackage : scanPackages) {
            String str = scanBasePackages + "." + moduleName + "." + scanPackage;
            dataFrom.append("//").append(str).append("\n");
        }
        // 写入文件，文件名使用模块名
        StringBuilder tsContent = new StringBuilder();
        tsContent.append("// 自动生成的TypeScript接口定义\n");
        tsContent.append("// 生成时间: ").append(new Date()).append("\n");
        tsContent.append("// 数据来源: ").append("\n").append(dataFrom).append("\n\n");

        // 按 scanPackages 数组顺序输出接口定义
        for (String packageName : scanPackages) {
            Map<String, String> packageInterfaces = PACKAGE_INTERFACE_DEFINITIONS.get(packageName);
            if (packageInterfaces != null && !packageInterfaces.isEmpty()) {
                // 可以添加包分隔注释
                tsContent.append("\n// ================ 包: ").append(packageName).append(" ================\n\n");
                for (Map.Entry<String, String> entry : packageInterfaces.entrySet()) {
                    tsContent.append(entry.getValue()).append("\n\n");
                }
            }
        }


        String moduleOutputPath = dir + "/" + moduleName + "." + fileType;
        try {
            writeToFile(tsContent.toString(), moduleOutputPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println("TypeScript接口定义已生成至: " + moduleOutputPath);
    }

    /**
     * 收集类的依赖关系
     */
    private static void collectClassDependencies(Class<?> clazz) {
        if (clazz == null || PROCESSED_CLASSES.contains(clazz) ||
                clazz.isPrimitive() || isBasicType(clazz)) {
            return;
        }

        // 添加到已处理集合
        PROCESSED_CLASSES.add(clazz);

        // 递归处理字段类型
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 忽略静态字段和 serialVersionUID
            if (isStaticField(field) || "serialVersionUID".equals(field.getName())) {
                continue;
            }

            // 忽略被 @JsonIgnore 标记的字段
            if (field.isAnnotationPresent(JsonIgnore.class)) {
                continue;
            }

            Class<?> fieldType = field.getType();

            // 处理普通类型
            if (!fieldType.isPrimitive() && !isBasicType(fieldType)) {
                if (isCollectionType(fieldType)) {
                    // 处理集合类型中的泛型参数
                    try {
                        if (field.getGenericType() instanceof ParameterizedType) {
                            ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                            for (Type typeArg : actualTypeArguments) {
                                if (typeArg instanceof Class) {
                                    collectClassDependencies((Class<?>) typeArg);
                                }
                            }
                        }
                    } catch (Exception e) {
                        // 忽略异常
                    }
                } else if (fieldType.isArray()) {
                    // 处理数组类型
                    collectClassDependencies(fieldType.getComponentType());
                } else {
                    // 处理自定义类型
                    collectClassDependencies(fieldType);
                }
            }
        }
    }

    /**
     * 获取指定包下的所有类
     */
    private static List<Class<?>> getClasses(String packageName) throws IOException, ClassNotFoundException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }

        List<Class<?>> classes = new ArrayList<>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes;
    }

    /**
     * 递归查找类文件
     */
    private static List<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class<?>> classes = new ArrayList<>();
        if (!directory.exists()) {
            return classes;
        }

        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    classes.addAll(findClasses(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                    try {
                        classes.add(Class.forName(className));
                    } catch (Exception e) {
                        // 忽略无法加载的类
                    }
                }
            }
        }
        return classes;
    }

    /**
     * 生成TypeScript接口定义
     */
    private static String generateInterface(Class<?> clazz) {
        if (clazz.isInterface() || clazz.isEnum()) {
            // 暂时不处理接口和枚举
            return "";
        }

        StringBuilder interfaceBuilder = new StringBuilder();

        // 添加注释，包含包名和类名
        interfaceBuilder.append("/**\n")
                .append(" * 对应Java类: ").append(clazz.getName()).append("\n")
                .append(" */\n");

        interfaceBuilder.append("export interface I").append(clazz.getSimpleName()).append(" {\n");

        Field[] fields = clazz.getDeclaredFields();
        boolean hasFields = false;

        for (Field field : fields) {
            // 忽略静态字段和 serialVersionUID
            if (isStaticField(field) || "serialVersionUID".equals(field.getName())) {
                continue;
            }

            // 忽略被 @JsonIgnore 标记的字段
            if (field.isAnnotationPresent(JsonIgnore.class)) {
                continue;
            }

            hasFields = true;

            // 获取字段名（支持@JsonProperty注解）
            String fieldName = getFieldName(field);

            interfaceBuilder.append("  ")
                    .append(fieldName);

            // 根据注解判断是否为可选字段
            boolean isRequired = isRequiredField(field);
            if (!isRequired) {
                interfaceBuilder.append("?");
            }

            interfaceBuilder.append(": ")
                    .append(mapToTypeScriptType(field, isRequired))
                    .append(";\n");
        }

        if (!hasFields) {
            // 添加空接口标记
            interfaceBuilder.append("  // 空接口\n");
        }

        interfaceBuilder.append("}");
        return interfaceBuilder.toString();
    }

    /**
     * 获取字段名（支持@JsonProperty注解）
     */
    private static String getFieldName(Field field) {
        if (field.isAnnotationPresent(JsonProperty.class)) {
            JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
            if (!jsonProperty.value().isEmpty()) {
                return jsonProperty.value();
            }
        }
        return field.getName();
    }

    /**
     * 判断字段是否为静态字段
     */
    private static boolean isStaticField(Field field) {
        return java.lang.reflect.Modifier.isStatic(field.getModifiers());
    }

    /**
     * 判断字段是否为必需字段
     *
     * @param field 字段
     * @return true表示必需，false表示可选
     */
    private static boolean isRequiredField(Field field) {
        // 如果有以下注解，则为必需字段@NotBlank
        if (field.isAnnotationPresent(NotBlank.class) ||
                field.isAnnotationPresent(NotEmpty.class) ||
                field.isAnnotationPresent(NotNull.class)) {
            return true;
        }

        // 如果有 @Null 注解，则为可选字段
        if (field.isAnnotationPresent(Null.class)) {
            return false;
        }

        // 默认情况下，字段是可选的
        return false;
    }

    /**
     * 将Java字段映射为TypeScript类型
     */
    private static String mapToTypeScriptType(Field field, boolean isRequired) {
        Class<?> fieldType = field.getType();
        String baseType = mapToTypeScriptType(fieldType, field);

        // 对于可选字段，类型可以是null
        if (!isRequired) {
            return baseType + " | null";
        }
        return baseType;
    }

    /**
     * 将Java类型映射为TypeScript类型
     */
    private static String mapToTypeScriptType(Class<?> javaType, Field field) {
        // 处理集合类型
        if (isCollectionType(javaType)) {
            // 尝试获取泛型参数类型
            String elementType = getCollectionElementType(field);
            if (elementType != null) {
                return elementType + "[]";
            } else {
                return "any[]";
            }
        }

        // 处理数组类型
        if (javaType.isArray()) {
            return mapToTypeScriptType(javaType.getComponentType(), (Field) null) + "[]";
        }

        // 处理基本类型和常见类型
        return mapSimpleTypeToTypeScript(javaType);
    }

    /**
     * 重载方法：将Java类型映射为TypeScript类型（不处理泛型）
     */
    private static String mapToTypeScriptType(Class<?> javaType) {
        // 处理数组类型
        if (javaType.isArray()) {
            return mapToTypeScriptType(javaType.getComponentType()) + "[]";
        }

        // 处理基本类型和常见类型
        return mapSimpleTypeToTypeScript(javaType);
    }

    /**
     * 判断是否为集合类型
     */
    private static boolean isCollectionType(Class<?> type) {
        return type.getName().equals("java.util.List") ||
                type.getName().equals("java.util.ArrayList") ||
                type.getName().equals("java.util.Set") ||
                type.getName().equals("java.util.HashSet") ||
                type.getName().equals("java.util.Collection") ||
                type.getName().equals("java.util.LinkedList") ||
                type.getName().equals("java.util.Vector");
    }

    /**
     * 获取集合元素的TypeScript类型
     */
    private static String getCollectionElementType(Field field) {
        try {
            // 检查字段是否包含泛型信息
            if (field.getGenericType() instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                if (actualTypeArguments.length > 0) {
                    Type elementType = actualTypeArguments[0];
                    if (elementType instanceof Class) {
                        Class<?> elementClass = (Class<?>) elementType;
                        return mapSimpleTypeToTypeScript(elementClass);
                    }
                }
            }
        } catch (Exception e) {
            // 忽略异常，返回null
        }
        return null;
    }

    /**
     * 映射简单类型到TypeScript类型
     */
    private static String mapSimpleTypeToTypeScript(Class<?> javaType) {
        // 基本类型和包装类型
        if (javaType == String.class) {
            return "string";
        } else if (javaType == int.class || javaType == Integer.class ||
                javaType == long.class || javaType == Long.class ||
                javaType == short.class || javaType == Short.class ||
                javaType == byte.class || javaType == Byte.class) {
            return "number";
        } else if (javaType == boolean.class || javaType == Boolean.class) {
            return "boolean";
        } else if (javaType == float.class || javaType == Float.class ||
                javaType == double.class || javaType == Double.class) {
            return "number";
        } else if (javaType == char.class || javaType == Character.class) {
            return "string";
        }

        // 大数字类型
        else if (javaType == BigInteger.class || javaType == BigDecimal.class) {
            return "number"; // 或者可以映射为 "string"，取决于业务需求
        }

        // 日期时间类型
        else if (javaType == Date.class ||
                javaType == LocalDateTime.class ||
                javaType == LocalDate.class ||
                javaType == LocalTime.class ||
                javaType == Instant.class ||
                javaType == ZonedDateTime.class) {
            return "string";
        }

        // UUID 类型
        else if (javaType == UUID.class) {
            return "string";
        }

        // 对于自定义类型，直接使用类名
        else if (!isBasicType(javaType)) {
            return "I" + javaType.getSimpleName();
        }

        // 其他情况默认为 any
        return "any";
    }

    /**
     * 判断是否为基本类型或常见类型
     */
    private static boolean isBasicType(Class<?> type) {
        return type.isPrimitive() ||
                type == String.class ||
                type == Integer.class ||
                type == Long.class ||
                type == Short.class ||
                type == Byte.class ||
                type == Boolean.class ||
                type == Float.class ||
                type == Double.class ||
                type == Character.class ||
                type == BigInteger.class ||
                type == BigDecimal.class ||
                type == Date.class ||
                type == LocalDateTime.class ||
                type == LocalDate.class ||
                type == LocalTime.class ||
                type == Instant.class ||
                type == ZonedDateTime.class ||
                type == UUID.class;
    }

    /**
     * 将内容写入文件
     */
    private static void writeToFile(String content, String filePath) throws IOException {
        File file = new File(filePath);
        file.getParentFile().mkdirs();

        try (FileWriter writer = new FileWriter(file)) {
            writer.write(content);
        }
    }
}